package location

import (
	"fmt"
	"time"
)

// LatLonDiffEpsilon 经纬度比较时的比较值
const LatLonDiffEpsilon = 0.0000005

// LatLonItf 经纬度接口
type LatLonItf interface {
	// 设置纬度
	SetLat(lat float64)
	// 获取纬度
	GetLat() float64
	// 设置经度
	SetLon(lon float64)
	// 获取经度
	GetLon() float64
	// 相等比较
	Equal(o *LatLon) bool
}

// LatLon 经纬度数据定义
type LatLon struct {
	Lat float64
	Lon float64
}

// SetLat 设置纬度
func (l *LatLon) SetLat(lat float64) {
	l.Lat = lat
}

// GetLat 获取纬度
func (l *LatLon) GetLat() float64 {
	return l.Lat
}

// SetLon 设置经度
func (l *LatLon) SetLon(lon float64) {
	l.Lon = lon
}

// GetLon 获取经度
func (l *LatLon) GetLon() float64 {
	return l.Lon
}

// Equal 相等比较
func (l *LatLon) Equal(o *LatLon) bool {
	if l == nil {
		return o == nil
	}
	if o == nil {
		return false
	}
	return l.Lat-o.Lat < LatLonDiffEpsilon && l.Lon-o.Lon < LatLonDiffEpsilon
}

// Cell 蜂窝基站信息
type Cell struct {
	// Mobile Country Code
	MCC int64
	// Mobile Network Code
	// CDMA network: System ID (SID)
	MNC int64
	// Location Area Code
	// CDMA network: Network ID (NID)
	LAC int64
	// Cell ID
	// CDMA network: Base Station ID (BID)
	CID int64
	// Signal Strength. unit: dBm
	Rssi int64
}

// Wifi Wifi 基站信息
type Wifi struct {
	// BSSID
	MacAddr string
	// Signal Strength. unit: dBm
	Rssi int64
	// he channel over which the client is communicating with the access point.
	Channel int64
	// The current signal to noise ratio measured in dB.
	Noise int64
	// Service Set Identifier
	SSID string
}

// NetConnectionType 网络连接类型
type NetConnectionType string

const (
	// NetConnTypeInvalid 无效的网络连接类型
	NetConnTypeInvalid NetConnectionType = ""
	// NetConnTypeCell 蜂窝网络
	NetConnTypeCell NetConnectionType = "0"
	// NetConnTypeWifi wifi 网络
	NetConnTypeWifi NetConnectionType = "1"
)

var netConnectionTypeNameMap = map[NetConnectionType]string{
	NetConnTypeCell: "CELL",
	NetConnTypeWifi: "WIFI",
}

func (x NetConnectionType) String() string {
	s, ok := netConnectionTypeNameMap[x]
	if ok {
		return s
	}
	return string(x)
}

// CellRadioType 蜂窝网络类型
type CellRadioType string

const (
	// CellRadioTypeInvalid 无效的蜂窝网络类型
	CellRadioTypeInvalid CellRadioType = ""
	// GSM Global System for Mobile Communications
	GSM CellRadioType = "GSM"
	// GPRS General Packet Radio Service
	GPRS CellRadioType = "GPRS"
	// EDGE Enhanced Data rates for GSM Evolution
	EDGE CellRadioType = "EDGE"
	// HSUPA High Speed Uplink Packet Access
	HSUPA CellRadioType = "HSUPA"
	// HSDPA ...
	HSDPA CellRadioType = "HSDPA"
	// WCDMA Wideband Code Division Multiple Access
	WCDMA CellRadioType = "WCDMA"
	// CDMA Code Division Multiple Access
	CDMA CellRadioType = "CDMA"
)

var cellRadioTypeNameMap = map[CellRadioType]string{
	GSM:   "GSM",
	GPRS:  "GPRS",
	EDGE:  "EDGE",
	HSUPA: "HSUPA",
	HSDPA: "HSDPA",
	WCDMA: "WCDMA",
	CDMA:  "CDMA",
}

// CellRadioTypeFromString 从 String 解析蜂窝网络类型
func CellRadioTypeFromString(name string) (CellRadioType, error) {
	typ := CellRadioType(name)
	_, ok := cellRadioTypeNameMap[typ]
	if !ok {
		return "", fmt.Errorf("unknown value %q for enum %s", name, name)
	}
	return typ, nil
}

// Station 基站信息
type Station struct {
	// 设备当前网络连接使用的类型
	NetConnType NetConnectionType

	// 蜂窝移动网络类型
	CellRadioType CellRadioType

	// IMSI，选填
	Imsi string

	// 蜂窝基站数据，主基站放到第一个
	Cells []*Cell

	// WiFi 基站数据，主基站放到第一个
	Wifis []*Wifi
}

// Address 地址信息
type Address struct {
	// 国家: 中国
	Country string
	// 省: 重庆市
	Province string
	// 城市名称: 重庆市
	City string
	// 城市编码: 023
	CityCode string
	// 区: 九龙坡区
	District string
	// 行政区编码: 500107 (对应到九龙坡区)
	AdCode string
	// 乡镇/街道（此街道为社区街道，不是道路信息）: 金凤镇
	Township string
	// 乡镇/街道编码: 500107104000 (对应到金凤镇)
	TownCode string
	// 街道名称: 凤笙路
	Street string
	// 道路名: 新州大道
	Road string
	// 附近的POI名称: 重庆美桀电子科技有限公司
	POI string
	// 地址描述
	Desc string
}

// GetCountry 国家
func (a *Address) GetCountry() string {
	if a == nil {
		return ""
	}
	return a.Country
}

// GetProvince 省
func (a *Address) GetProvince() string {
	if a == nil {
		return ""
	}
	return a.Province
}

// GetCity 城市名称
func (a *Address) GetCity() string {
	if a == nil {
		return ""
	}
	return a.City
}

// GetCityCode 城市编码
func (a *Address) GetCityCode() string {
	if a == nil {
		return ""
	}
	return a.CityCode
}

// GetDistrict 区
func (a *Address) GetDistrict() string {
	if a == nil {
		return ""
	}
	return a.District
}

// GetAdCode 行政区编码
func (a *Address) GetAdCode() string {
	if a == nil {
		return ""
	}
	return a.AdCode
}

// GetTownship 乡镇/街道
func (a *Address) GetTownship() string {
	if a == nil {
		return ""
	}
	return a.Township
}

// GetTownCode 乡镇/街道编码
func (a *Address) GetTownCode() string {
	if a == nil {
		return ""
	}
	return a.TownCode
}

// GetStreet 街道名称
func (a *Address) GetStreet() string {
	if a == nil {
		return ""
	}
	return a.Street
}

// GetRoad 道路名
func (a *Address) GetRoad() string {
	if a == nil {
		return ""
	}
	return a.Road
}

// GetPOI 附近的POI名称
func (a *Address) GetPOI() string {
	if a == nil {
		return ""
	}
	return a.POI
}

// GetDesc 地址描述
func (a *Address) GetDesc() string {
	if a == nil {
		return ""
	}
	return a.Desc
}

// Type 定位类型
type Type int

const (
	// GPS GPS 方式
	GPS Type = 1
	// BDS 北斗
	BDS Type = 2
	// GALILEO 伽利略
	GALILEO Type = 3
	// GLONASS 格洛纳斯
	GLONASS Type = 4
	// CELL 蜂窝定位
	CELL Type = 5
	// WIFI WiFi定位
	WIFI Type = 6
	// BT 蓝牙定位
	BT Type = 7
	// HYBRID 混合定位
	HYBRID Type = 8
)

// Position 位置信息
type Position struct {
	// 经纬度（WGS-84）
	Wgs84 LatLon
	// 国测距的坐标系（GCJ-02）
	Gcj02 LatLon
	// 定位精度。单位:米
	Accuracy float64
	// 定位类型
	LocationType Type
	// 定位时间
	Time time.Time

	// 地理信息
	Addr *Address
}

// ErrCode 错误码
type ErrCode int

const (
	// ErrSuccess 成功
	ErrSuccess ErrCode = 0
	// ErrFailure 普通失败
	ErrFailure ErrCode = 1
	// ErrInvalidParam 无效参数
	ErrInvalidParam ErrCode = 2
)

// Location 位置相关操作接口
type Location interface {
	Lbs(netConnType NetConnectionType, cellRadioType CellRadioType, imei, imsi string, cells []*Cell, wifis []*Wifi) (errCode ErrCode, position *Position)
	LbIP(ip string) (errCode ErrCode, position *Position)
	Regeocode(position *Position, accuracy float64) (errCode ErrCode, addr *Address)
}

var locationImpl Location

func init() {
	locationImpl = &amap{}
}

// Lbs 基站定位
func Lbs(netConnType NetConnectionType, cellRadioType CellRadioType, imei, imsi string, cells []*Cell, wifis []*Wifi) (errCode ErrCode, position *Position) {
	if netConnType == NetConnTypeInvalid || (len(cells) == 0 && len(wifis) == 0) || (len(cells) != 0 && cellRadioType == CellRadioTypeInvalid) {
		return ErrInvalidParam, nil
	}
	now := time.Now()
	errCode, position = locationImpl.Lbs(netConnType, cellRadioType, imei, imsi, cells, wifis)
	if errCode == ErrSuccess {
		position.Time = now
	}
	return
}

// LbIP IP 定位
func LbIP(ip string) (errCode ErrCode, position *Position) {
	if len(ip) == 0 {
		return ErrInvalidParam, nil
	}
	now := time.Now()
	errCode, position = locationImpl.LbIP(ip)
	if errCode == ErrSuccess {
		position.Time = now
	}
	return
}

// Regeocode 逆地理编码
func Regeocode(position *Position, accuracy float64) (errCode ErrCode, addr *Address) {
	if position == nil {
		return ErrInvalidParam, nil
	}
	return locationImpl.Regeocode(position, accuracy)
}

// NormalizationCellRssi 规范化蜂窝基站的信号值
func NormalizationCellRssi(rssi int64) int64 {
	if rssi > 0 {
		rssi = rssi*2 - 113
	}
	return rssi
}
